package com.jim.director.framework.ioc;

import com.jim.director.framework.annotation.Actor;
import com.jim.director.framework.annotation.ActorAutowired;
import com.jim.director.framework.annotation.Configure;
import com.jim.director.framework.tool.PackageScanner;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.util.*;

/**
 * author: Jim
 * date: 2024/10/2
 * info:
 */

public class ActorFactory {

    /**
     * 所有的Actor信息都会被注册在这里
     */
    private Map<String, ActorDefinition> actorDefinitionRegistry;

    /**
     * 单例Actor实例会存储在这里 (size = 1)
     */
    private Map<String, Object> actorRegistry;

    /**
     * 多例Actor集合实例会存储在这里 (size > 1)
     */
    private Map<String, Collection<Object>> actorCollectionRegistry;

    public ActorFactory(){
        actorDefinitionRegistry = new HashMap<>();
        actorRegistry = new HashMap<>();
        actorCollectionRegistry = new HashMap<>();
    }

    public void loadActors(Class<?> starter) throws Exception{
        registerActors(starter);
        instanceActors();
        injectDependencies();
    }

    /**
     * 注册所有Actor
     * @throws Exception
     */
    private void registerActors(Class<?> starter) throws Exception {
        //扫描启动类路径下的所有包
        List<Class<?>> classes = PackageScanner.scanPackage(starter);
        //注册Actor
        for(Class<?> clazz : classes){
            if(clazz.isAnnotationPresent(Actor.class)){
                Actor actor = clazz.getAnnotation(Actor.class);
                actorDefinitionRegistry.put(clazz.getName(), createActorDefinition(clazz, actor.size()));
            }
            else if(clazz.isAnnotationPresent(Configure.class)){
                actorDefinitionRegistry.put(clazz.getName(), createActorDefinition(clazz, 1));
            }
        }
    }

    /**
     * 实例化所有Actor
     * @throws Exception
     */
    private void instanceActors() throws Exception{
        //扫描ActorDefinitionRegistry，优先实例化Configure
        for(ActorDefinition actorDefinition : actorDefinitionRegistry.values()){
            if(actorDefinition.getActorClass().isAnnotationPresent(Configure.class)){
                createAndRegisterActor(actorDefinition);
            }
        }
        //扫描ActorDefinitionRegistry，实例化其他Actor
        for(ActorDefinition actorDefinition : actorDefinitionRegistry.values()){
            if(!actorDefinition.getActorClass().isAnnotationPresent(Configure.class)){
                createAndRegisterActor(actorDefinition);
            }
        }
    }

    /**
     * 根据ActorDefinition创建并注册Actor实例
     * @param actorDefinition
     * @throws Exception
     */
    private void createAndRegisterActor(ActorDefinition actorDefinition) throws Exception {
        Class<?> actorClass = actorDefinition.getActorClass();
        int size = actorDefinition.getSize();

        if(size == 1) {
            // 单例模式，创建单个实例
            Object actorInstance = createActor(actorDefinition);
            actorRegistry.put(actorClass.getName(), actorInstance);
        } else {
            // 多例模式，创建实例集合
            List<Object> actorCollection = new ArrayList<>();
            Constructor<?> constructor = actorClass.getDeclaredConstructor();
            for(int i = 0; i < size; i++){
                Object actorInstance = constructor.newInstance();
                actorCollection.add(actorInstance);
            }
            actorCollectionRegistry.put(actorClass.getName(), actorCollection);
        }
    }

    /**
     * 对所有Actor实例进行依赖注入
     * @throws Exception
     */
    private void injectDependencies() throws Exception{
        //对单例Actor进行依赖注入
        for(Object actor : actorRegistry.values()){
            injectDependency(actor);
        }
        //对多例Actor进行依赖注入
        for(Collection<Object> actorCollection : actorCollectionRegistry.values()){
            for(Object actor : actorCollection){
                injectDependency(actor);
            }
        }
    }

    /**
     * 对当前Actor实例进行依赖注入
     * @param actor
     * @throws Exception
     */
    private void injectDependency(Object actor)  throws Exception{
        Field[] fields = actor.getClass().getDeclaredFields();
        for(Field field : fields){
            if(field.isAnnotationPresent(ActorAutowired.class)){
                injectActorField(actor, field);
            }
        }
    }

    /**
     * 注入Actor字段
     * @param actor 当前Actor实例
     * @param field 要注入的字段
     * @throws Exception
     */
    private void injectActorField(Object actor, Field field) throws Exception {
        Class<?> fieldType = field.getType();
        field.setAccessible(true);

        // 判断字段类型是否为集合
        if(Collection.class.isAssignableFrom(fieldType)){
            // 字段是集合类型，注入Actor集合
            injectActorCollection(actor, field);
        } else {
            // 字段是单个对象类型，注入单个Actor
            injectSingleActor(actor, field);
        }
    }

    /**
     * 注入单个Actor
     * @param actor 当前Actor实例
     * @param field 要注入的字段
     * @throws Exception
     */
    private void injectSingleActor(Object actor, Field field) throws Exception {
        Class<?> fieldType = field.getType();

        // 优先从单例注册表中查找
        Object dependency = actorRegistry.get(fieldType.getName());
        if(dependency != null){
            field.set(actor, dependency);
            return;
        }

        // 如果单例注册表中没有，检查多例注册表
        Collection<Object> actorCollection = actorCollectionRegistry.get(fieldType.getName());
        if(actorCollection != null && !actorCollection.isEmpty()){
            // 从多例集合中取第一个实例
            Object firstInstance = actorCollection.iterator().next();
            field.set(actor, firstInstance);
            return;
        }

        throw new Exception("Can not inject dependency: " + field.getName() + " of type " + fieldType.getName());
    }

    /**
     * 注入Actor集合
     * @param actor 当前Actor实例
     * @param field 要注入的字段
     * @throws Exception
     */
    private void injectActorCollection(Object actor, Field field) throws Exception {
        // 获取字段泛型类型
        ParameterizedType genericType = (ParameterizedType) field.getGenericType();
        Class<?> genericClass = (Class<?>) genericType.getActualTypeArguments()[0];

        // 优先从多例注册表中查找
        Collection<Object> actorCollection = actorCollectionRegistry.get(genericClass.getName());
        if(actorCollection != null){
            // 创建与字段类型匹配的集合实例
            Collection<Object> targetCollection = createCollectionInstance(field.getType(), actorCollection.size());
            targetCollection.addAll(actorCollection);
            field.set(actor, targetCollection);
            return;
        }

        // 如果多例注册表中没有，检查单例注册表
        Object singleActor = actorRegistry.get(genericClass.getName());
        if(singleActor != null){
            // 将单例Actor包装成集合
            Collection<Object> targetCollection = createCollectionInstance(field.getType(), 1);
            targetCollection.add(singleActor);
            field.set(actor, targetCollection);
            return;
        }

        throw new Exception("Can not inject dependency: " + field.getName() + " with generic type " + genericClass.getName());
    }

    /**
     * 创建集合实例
     * @param collectionType 集合类型
     * @param size 初始大小
     * @return 集合实例
     * @throws Exception
     */
    private Collection<Object> createCollectionInstance(Class<?> collectionType, int size) throws Exception {
        if(List.class.isAssignableFrom(collectionType)){
            return new ArrayList<>(size);
        } else if(Set.class.isAssignableFrom(collectionType)){
            return new HashSet<>(size);
        } else {
            // 尝试使用反射创建实例
            try {
                return (Collection<Object>) collectionType.getDeclaredConstructor(int.class).newInstance(size);
            } catch (Exception e) {
                return (Collection<Object>) collectionType.getDeclaredConstructor().newInstance();
            }
        }
    }

    /**
     * 创建ActorDefinition实例
     * @param clazz
     * @param size
     * @return
     */
    private ActorDefinition createActorDefinition(Class<?> clazz, int size){
        return new ActorDefinition(clazz, size);
    }

    /**
     * 创建Actor实例
     * @param actorDefinition
     * @return
     * @throws NoSuchMethodException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     * @throws InstantiationException
     */
    private Object createActor(ActorDefinition actorDefinition) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        return actorDefinition.getActorClass().getDeclaredConstructor().newInstance();
    }

    public Map<String, ActorDefinition> getActorDefinitionRegistry() {
        return actorDefinitionRegistry;
    }

    public void setActorDefinitionRegistry(Map<String, ActorDefinition> actorDefinitionRegistry) {
        this.actorDefinitionRegistry = actorDefinitionRegistry;
    }

    public Map<String, Object> getActorRegistry() {
        return actorRegistry;
    }

    public void setActorRegistry(Map<String, Object> actorRegistry) {
        this.actorRegistry = actorRegistry;
    }

    public Map<String, Collection<Object>> getActorCollectionRegistry() {
        return actorCollectionRegistry;
    }

    public void setActorCollectionRegistry(Map<String, Collection<Object>> actorCollectionRegistry) {
        this.actorCollectionRegistry = actorCollectionRegistry;
    }
}
